package cheme.ProcessModel.Cmds;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.gef.commands.Command;

import cheme.ProcessModel.IChild;
import cheme.ProcessModel.IParent;
import cheme.ProcessModel.ProcessUnits.IProcessUnit;
import cheme.ProcessModel.ProcessUnits.Streams.AbstractStream;


public class DeleteProcessUnitCmd extends Command
{
	private IParent parent;

	private IProcessUnit child;

	private List<DeleteStreamCmd> streamDeleteCmds;
	
	public DeleteProcessUnitCmd(IProcessUnit processUnit)
	{
		this.child = processUnit;
		this.parent = this.child.getParent();
	}
	
	/*
	 * Since this command may remove streams, and streams may be attached to 
	 * terminals (which automatically die when all of their stream is removed)
	 * it needs to do something special prior to being removed from the parent.
	 * 
	 * There are a few options:
	 * - Create a stream proxy that bothers with looking at the other end of the 
	 * stream and removing/adding terminals there as needed.
	 * - Chaining (in some way) a stream delete command for each stream. The 
	 * stream delete already handles removing terminals. This has the benefit of
	 * simplifying other process unit delete commands (I am thinking the terminal 
	 * delete commands) as they can just make a delete of the attached streams.
	 * 
	 * (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	
	private List<DeleteStreamCmd> getStreamDeleteCmds()
	{
		if(null == streamDeleteCmds)
		{
			streamDeleteCmds = new ArrayList<DeleteStreamCmd>();
			
			List<IChild> streams = child.getOutStreams();
			IChild current;
			
			for(int i = 0; i < streams.size(); i++)
			{
				current = streams.get(i);
				streamDeleteCmds.add(new DeleteStreamCmd((AbstractStream)current));
			}
			
			streams = child.getInStreams();
			
			for(int i = 0; i < streams.size(); i++)
			{
				current = streams.get(i);
				streamDeleteCmds.add(new DeleteStreamCmd((AbstractStream)current));
			}
		}
		
		return streamDeleteCmds;
	}
	
	private void executeStreamDeletes()
	{
		List<DeleteStreamCmd> streams = getStreamDeleteCmds();
		DeleteStreamCmd current;
		for(int i = 0; i < streams.size(); i++)
		{
			current = streams.get(i);
			current.execute();
		}
	}
	
	private void undoStreamDeletes()
	{
		List<DeleteStreamCmd> streams = getStreamDeleteCmds();
		DeleteStreamCmd current;
		for(int i = streams.size() - 1; i >= 0; i--)
		{
			current = streams.get(i);
			current.undo();
		}
	}

	public void execute()
	{
		executeStreamDeletes();
		
		/*
		 * Note that the above invocation of stream delete commands may remove 
		 * the PU (for instance, if it is a ITerminal) before this statement 
		 * tries to manually remove it. In such a case this statement has no 
		 * effects at all.
		 * 
		 * Generally it will add a process unit to the specified parent.
		 */
		
		/*
		 * Since the unit may already be removed.
		 */
		
		if(parent.contains(child))
		{
			parent.remove(child);
		}	
	}

	public void undo()
	{
		undoStreamDeletes();
		
		/*
		 * Note that the above invocation of stream delete-undo commands may  
		 * add the PU (for instance, if it is a ITerminal) before this  
		 * statement tries to manually add it. In such a case this statement  
		 * has no effects at all.
		 * 
		 * Generally it will add a process unit to the specified parent.
		 */
		
		/*
		 * Since the unit may already be added.
		 */
		
		if(!parent.contains(child))
		{
			parent.add(child);
		}
	}
}